package promotion

import (
	"fmt"

	enum "mall/service/backend/common/enum/promotion"
	"mall/service/forum/api/internal/types"

	"github.com/shopspring/decimal"
	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

// ActivityDiscount 具体行为
// 满减送类型
type ActivityDiscount struct {
	Activity
}

// checkParams 参数校验(不同优惠活动类型实现不同)
func (activity *ActivityDiscount) checkParams(ctx *Context) (err error) { ///
	logx.Infow("满减送类型:特殊参数校验...")

	return
}

// getPricingInfo 获取定价信息(不同优惠活动类型实现不同)
func (activity *ActivityDiscount) getPricingInfo(ctx *Context) (err error) {
	// logx.Infow("满减送类型:获取定价信息...")

	if len(ctx.PricingInfos) == 0 || len(ctx.OrderPricingInfos) == 0 {
		return
	}

	var checkMutex bool
	if ctx.ActInfo.MutexType != nil && uint8(*ctx.ActInfo.MutexType) == enum.PromotionMutexTypeSku {
		checkMutex = true
	}

	for _, info := range ctx.PricingInfos {
		if checkMutex {
			if subMap, ok := ctx.MutexMap[*ctx.ActInfo.MutexType]; ok {
				if _, ok := subMap[info.SkuId]; ok { // 已存在，跳过后续优惠
					continue
				}
			}

			ctx.MutexMap[*ctx.ActInfo.MutexType] = map[uint64]int64{info.SkuId: 1}
		}
	}

	return
}

func (activity *ActivityDiscount) getOrderPricingInfo(ctx *Context) (err error) {
	if len(ctx.PricingInfos) == 0 || len(ctx.OrderPricingInfos) == 0 {
		return
	}

	if len(ctx.ActInfo.Groups) == 0 {
		return
	}

	var groupMode uint8
	if ctx.ActInfo.GroupMode != nil {
		groupMode = uint8(*ctx.ActInfo.GroupMode)
	}

	var groupType uint8
	if ctx.ActInfo.GroupType != nil {
		groupType = uint8(*ctx.ActInfo.GroupType)
	}

	// 检查
	switch groupType {
	case enum.PromotionActivityRuleGroupOne: // 阶梯满减
		err = activity.getOrderPricingInfoByGroup(ctx, groupMode) // Todo: review
	case enum.PromotionActivityRuleGroupMany: // 循环满减
		err = activity.getOrderPricingInfoByGroups(ctx, groupMode) // Todo: review
	}

	if err != nil {
		return err
	}

	return
}

func (activity *ActivityDiscount) checkThreshold(
	ctx *Context,
	threshhold decimal.Decimal,
	groupMode uint8,
) (bool, string, string) {
	var gemOrNum string
	switch groupMode {
	case enum.PromotionActivityRuleGroupAmount: // 满N元减送
		// gemOrNum = threshhold.Mul(ctx.GemRatio).Truncate(2).String()
		gemOrNum = threshhold.Truncate(2).String()
		if ctx.OrderPricingInfos[0].SkusAmount.GreaterThan(threshhold) {
			return true, gemOrNum, ""
		}

		// return false, gemOrNum, threshhold.Sub(ctx.OrderPricingInfos[0].SkusAmount).Mul(ctx.GemRatio).Truncate(2).String()
		return false, gemOrNum, threshhold.Sub(ctx.OrderPricingInfos[0].SkusAmount).Truncate(2).String()
	case enum.PromotionActivityRuleGroupNumber: // 满N件减送
		skusNumber := decimal.NewFromInt(ctx.OrderPricingInfos[0].SkusNumber)
		gemOrNum = threshhold.Truncate(0).String()
		if skusNumber.GreaterThanOrEqual(threshhold) {
			return true, gemOrNum, ""
		}

		return false, gemOrNum, threshhold.Sub(skusNumber).Truncate(0).String()
	}

	return false, gemOrNum, ""
}

func (activity *ActivityDiscount) getOrderPricingInfoBySpecificGroupRule(
	ctx *Context,
	idx int,
	idy int,
	rule *types.PromotionActivityRuleInfo,
) (err error) {
	var ruleMode uint8
	if rule.Mode != nil {
		ruleMode = uint8(*rule.Mode)
	}

	if rule.RuleType != nil {
		switch uint8(*rule.RuleType) {
		case enum.PromotionActivityRuleTypeDiscount: // 订单金额优惠
			err = activity.getOrderPricingInfoByRuleDiscount(ctx, idx, idy, rule, ruleMode)
		case enum.PromotionActivityRuleTypeFreeShipping: // 包邮
			err = activity.getOrderPricingInfoByRuleFreeShipping(ctx, idx, idy, rule, ruleMode)
		case enum.PromotionActivityRuleTypeExtraPoint: // 额外赠送积分
			err = activity.getOrderPricingInfoByRuleExtraPoint(ctx, idx, idy, rule, ruleMode)
		case enum.PromotionActivityRuleTypeCoupon: // 送优惠券
			err = activity.getOrderPricingInfoByRuleCoupon(ctx, idx, idy, rule, ruleMode)
		}

		if err != nil {
			return err
		}
	}

	return
}

func (activity *ActivityDiscount) getOrderPricingInfoByRuleDiscount(
	ctx *Context,
	idx int,
	idy int,
	rule *types.PromotionActivityRuleInfo,
	ruleMode uint8,
) (err error) {
	if details, ok := ctx.OrderPricingDetails[idx]; ok {
		if f, err := decimal.NewFromString(*rule.Value); err == nil {
			switch ruleMode {
			case enum.PromotionActivityRuleModeAmount: // 满减
				if ctx.OrderPricingInfos[idx].FinalAmount.GreaterThan(f) {
					details[idy].ActivityAmount = f
					details[idy].ActivityDiscount = f.Div(ctx.OrderPricingInfos[idx].FinalAmount)
					details[idy].Status = 1
					ctx.OrderPricingDetails[idx] = details
					ctx.OrderPricingInfos[idx].FinalAmount = ctx.OrderPricingInfos[idx].FinalAmount.Sub(f)
					err = activity.distributePricingInfo(ctx, idx, idy)
				} else {
					logrus.Info(fmt.Sprintf("ActivityDiscount getOrderPricingInfoByRuleDiscount Discount GreaterThan Amount: %+v > %+v",
						f, ctx.OrderPricingInfos[idx].FinalAmount))
				}
			case enum.PromotionActivityRuleModeDiscount: // 满折
				var oneHundredDecimal decimal.Decimal = decimal.NewFromInt(100)
				if oneHundredDecimal.GreaterThan(f) {
					details[idy].ActivityAmount =
						ctx.OrderPricingInfos[0].SkusAmount.Mul(oneHundredDecimal.Sub(f)).Div(oneHundredDecimal)
					details[idy].ActivityDiscount = oneHundredDecimal.Sub(f)
					details[idy].Status = 1
					ctx.OrderPricingDetails[idx] = details
					ctx.OrderPricingInfos[idx].FinalAmount =
						ctx.OrderPricingInfos[idx].FinalAmount.Sub(details[idy].ActivityAmount)
					err = activity.distributePricingInfo(ctx, idx, idy)
				} else {
					logrus.Info(fmt.Sprintf("ActivityDiscount getOrderPricingInfoByRuleDiscount Discount GreaterThan 100: %+v", f))
				}
			case enum.PromotionActivityRuleModeFixed: // 固定金额（如3件9.9元）
				details[idy].ActivityFixed = f
				details[idy].Status = 1
				ctx.OrderPricingDetails[idx] = details
				err = activity.distributePricingInfoPartially(ctx, idx, idy)
			}
			if err != nil {
				return err
			}
		}
	}

	return
}

func (activity *ActivityDiscount) distributePricingInfo(ctx *Context, idx int, idy int) (err error) {
	if details, ok := ctx.OrderPricingDetails[idx]; ok {
		groupPricingInfo := details[idy]

		for k, info := range ctx.PricingInfos {
			diff := info.FinalAmount.Mul(groupPricingInfo.ActivityDiscount)
			info.ActivityAmount = info.ActivityAmount.Add(diff)
			info.FinalAmount = info.FinalAmount.Sub(diff)
			ctx.PricingInfos[k] = info
		}
	}

	return
}

func (activity *ActivityDiscount) distributePricingInfoPartially(ctx *Context, idx int, idy int) (err error) {
	if details, ok := ctx.OrderPricingDetails[idx]; ok {
		groupPricingInfo := details[idy]
		var limit int64 = groupPricingInfo.Limit.IntPart()
		var cnt int64 = limit
		var skusAmount decimal.Decimal

		for _, info := range ctx.PricingInfos {
			if cnt <= 0 {
				break
			}

			if info.SkuNumber <= cnt {
				skusAmount = skusAmount.Add(info.SkuAmount)
			} else {
				skusAmount = skusAmount.Add(info.SkuPrice.Mul(decimal.NewFromInt(cnt)))
			}
			cnt -= info.SkuNumber
		}

		var activityDiscount decimal.Decimal = groupPricingInfo.ActivityFixed.Div(skusAmount)
		diff := skusAmount.Sub(groupPricingInfo.ActivityFixed)
		details[idy].ActivityAmount = details[idy].ActivityAmount.Add(diff)
		ctx.OrderPricingDetails[idx] = details
		ctx.OrderPricingInfos[idx].ActivityAmount = ctx.OrderPricingInfos[idx].ActivityAmount.Add(diff)
		ctx.OrderPricingInfos[idx].FinalAmount = ctx.OrderPricingInfos[idx].FinalAmount.Sub(diff)
		activityDiscount = decimal.NewFromInt(1).Sub(activityDiscount) // 可以为负值
		cnt = limit
		for k, info := range ctx.PricingInfos {
			if cnt <= 0 {
				break
			}

			if info.SkuNumber <= cnt {
				amount := info.SkuAmount.Mul(activityDiscount)
				ctx.PricingInfos[k].ActivityAmount = info.ActivityAmount.Add(amount)
				ctx.PricingInfos[k].FinalAmount = info.FinalAmount.Sub(amount)
			} else {
				amount := info.SkuPrice.Mul(decimal.NewFromInt(cnt)).Mul(activityDiscount)
				ctx.PricingInfos[k].ActivityAmount = info.ActivityAmount.Add(amount)
				ctx.PricingInfos[k].FinalAmount = info.FinalAmount.Sub(amount)
			}
			cnt -= info.SkuNumber
		}
	}

	return
}

func (activity *ActivityDiscount) getOrderPricingInfoByRuleFreeShipping(
	ctx *Context,
	idx int,
	idy int,
	rule *types.PromotionActivityRuleInfo,
	ruleMode uint8,
) (err error) {
	if details, ok := ctx.OrderPricingDetails[idx]; ok {
		switch ruleMode {
		case enum.PromotionActivityRuleModeAmount: // 整单包邮
			details[idy].Effect = enum.PromotionEffectOrderFreeShipping
			details[idy].EffectType = enum.PromotionEffectTypeFreeShipping
			details[idy].Status = 0
			ctx.OrderPricingDetails[idx] = details
		case enum.PromotionActivityRuleModeDiscount: // 仅活动商品包邮
			details[idy].Effect = enum.PromotionEffectProductFreeShipping
			details[idy].EffectType = enum.PromotionEffectTypeFreeShipping
			details[idy].Status = 0
			ctx.OrderPricingDetails[idx] = details
		}
		if err != nil {
			return err
		}
	}

	return
}

func (activity *ActivityDiscount) getOrderPricingInfoByRuleExtraPoint(
	ctx *Context,
	idx int,
	idy int,
	rule *types.PromotionActivityRuleInfo,
	ruleMode uint8,
) (err error) {
	// todo

	return
}

func (activity *ActivityDiscount) getOrderPricingInfoByRuleCoupon(
	ctx *Context,
	idx int,
	idy int,
	rule *types.PromotionActivityRuleInfo,
	ruleMode uint8,
) (err error) {
	// todo

	return
}

func (activity *ActivityDiscount) getOrderPricingInfoBySpecificGroup(
	ctx *Context,
	idx int,
	group *types.PromotionActivityRuleGroupInfo,
) (err error) { //
	if len(group.Rules) > 0 {
		var idy int
		for _, rule := range group.Rules {
			ctx.OrderPricingDetails[idx] = append(ctx.OrderPricingDetails[idx], &OrderPricingDetailInfo{
				ActivityId: ctx.OrderPricingInfos[idx].ActivityId,
				Threshold:  ctx.OrderPricingInfos[idx].Threshold,
				Limit:      ctx.OrderPricingInfos[idx].Limit,
				RuleId:     int64(*rule.Id),
			})
			err := activity.getOrderPricingInfoBySpecificGroupRule(ctx, idx, idy, rule)
			if err != nil {
				return err
			}

			idy += 1
		}
	}

	return
}

func (activity *ActivityDiscount) getOrderPricingInfoByGroup(ctx *Context, groupMode uint8) (err error) {
	var idx int
	ctx.OrderPricingDetails = make(map[int][]*OrderPricingDetailInfo)
	for _, group := range ctx.ActInfo.Groups {
		// logrus.Info(fmt.Sprintf("ActivityDiscount getOrderPricingInfoByGroup: %+v", group))
		if group.Threshold != nil {
			if f, err := decimal.NewFromString(*group.Threshold); err == nil {
				fulfilled, threshold, margin := activity.checkThreshold(ctx, f, groupMode)
				ctx.OrderPricingInfos = append(ctx.OrderPricingInfos, &OrderPricingInfo{
					SkusNumber:  ctx.OrderPricingInfos[0].SkusNumber,
					SkusAmount:  ctx.OrderPricingInfos[0].SkusAmount,
					FinalAmount: ctx.OrderPricingInfos[0].FinalAmount,
					ActivityId:  int64(*ctx.ActInfo.Id),
					Threshold:   threshold,
					Limit:       f,
					Status:      1,
				})
				idx += 1
				if fulfilled {
					return activity.getOrderPricingInfoBySpecificGroup(ctx, idx, group)
				}

				ctx.OrderPricingInfos[idx].Status = -1
				ctx.OrderPricingInfos[idx].Margin = margin
			}
		}
	}

	return
}

func (activity *ActivityDiscount) getOrderPricingInfoByGroups(ctx *Context, groupMode uint8) (err error) {
	return
}
